package es.aqua.aquac.il.dal.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;

import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.springframework.context.annotation.Configuration;
import org.springframework.orm.jpa.JpaCallback;
import org.springframework.orm.jpa.support.JpaDaoSupport;

import es.aqua.aquac.exception.GeneralDAOException;
import es.aqua.aquac.il.dal.interfaces.GeneralDAO;
import es.aqua.aquac.il.domain.GeneralDomainObject;

@Configuration("GeneralDAO")
public class GeneralDAOImpl<E extends GeneralDomainObject> extends JpaDaoSupport implements GeneralDAO<E> {

	private Class<?> persistentClass;

	private EntityManager entityManager;

	/**
	 * @return the entityManager
	 */
	public EntityManager getEntityManager() {
		return entityManager;
	}

	public GeneralDAOImpl() {
		this.persistentClass = (Class<?>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
	}

	public E insert(E entity) throws GeneralDAOException {

		try {
			getJpaTemplate().persist(entity);

			return entity;
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-INSERT", "Se ha producido un error al insertar el Registro de tipo '"
					+ entity.getClass().getCanonicalName() + "'");
		}

	}

	public void update(E entity) throws GeneralDAOException {
		try {
			getJpaTemplate().merge(entity);
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-UPDATE", "Se ha producido un error al insertar el Registro de tipo '"
					+ entity.getClass().getCanonicalName() + "'");
		}

	}

	public void remove(E entity) throws GeneralDAOException {
		try {
			getJpaTemplate().remove(entity);
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-REMOVE", "Se ha producido un error al borrar el Registro de tipo '"
					+ entity.getClass().getCanonicalName() + "'");
		}

	}

	public E findById(Serializable id) throws GeneralDAOException {
		try {
			return (E) getJpaTemplate().find(persistentClass, id);
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-FINDBYID", "Se ha producido un error al buscar por ID. Entidad persistentClass '"
					+ persistentClass.getCanonicalName() + "'");
		}
	}

	public List<E> findByCriteria(E entity, final String prop, final Boolean asc) throws GeneralDAOException {
		final E t = entity;
		try {

			List<E> list = (List<E>) getJpaTemplate().execute(new JpaCallback<Object>() {
				public Object doInJpa(javax.persistence.EntityManager em) throws javax.persistence.PersistenceException {
					final Example example = Example.create(t).excludeZeroes().ignoreCase();

					Criteria criteria = ((Session) em.getDelegate()).createCriteria(t.getClass()).add(example);
					order(criteria, prop, asc);
					return criteria.list();
				}
			});

			return list;
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-FINDBYCRITERIA", "Se ha producido un error al buscar por Criterios. Entidad persistentClass '"
					+ persistentClass.getCanonicalName() + "'");
		}
	}

	public List<E> findAll() throws GeneralDAOException {
		List<E> entities = new ArrayList<E>();
		try {
			String s = "select c from " + persistentClass.getSimpleName() + " c where c.borradoLogico = 0";
			entities = getJpaTemplate().find(s);
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-FINDALL", e.getMessage());
		}
		return entities;
	}

	public List<E> findAllOrderBy(String prop, Boolean asc) throws GeneralDAOException {
		List<E> entities = new ArrayList<E>();
		try {
			String s = "select c from " + persistentClass.getSimpleName() + " c where c.borradoLogico = 0 order by c." + prop
					+ (asc ? " asc" : " desc");
			entities = getJpaTemplate().find(s);
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-FINDALLORDERBY", "Se ha producido un error al buscar todas las entidades.Entidad persistentClass '"
					+ persistentClass.getCanonicalName() + "'");
		}
		return entities;
	}

	public List<E> findByProperty(String prop, String value) throws GeneralDAOException {
		List<E> entities = new ArrayList<E>();
		try {
			String s = "select c from " + persistentClass.getSimpleName() + "  c where c.borradoLogico = 0 and c." + prop + " = '" + value + "'";
			entities = getJpaTemplate().find(s);
		} catch (Exception e) {
			throw new GeneralDAOException("IL-DAL-FINDBYPROPERTY", "Se ha producido un error al buscar todas las entidades.Entidad persistentClass '"
					+ persistentClass.getCanonicalName() + "'");
		}
		return entities;

	}

	private void order(Criteria criteria, String prop, Boolean asc) {

		Order ord = null;
		if (prop != null) {
			if (asc) {
				ord = Order.asc(prop);
			} else {
				ord = Order.desc(prop);
			}
			criteria.addOrder(ord);
		}
	}
}
